﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace HeroldIT.Dnn.JQuery.UI
{
    /// <summary>
    /// Creates a jQuery UI Progressbar widget.
    /// </summary>
    [ParseChildren(true)]
    public class Progressbar : WebControl
    {
        #region properties

        /// <summary>
        /// Gets or sets the name of the theme to use for the control.
        /// </summary>
        public string ThemeName { get; set; }

        /// <summary>
        /// Gets or sets whether to automatically register jQuery UI on the page.
        /// </summary>
        [Browsable(true)]
        [Localizable(false)]
        [EditorBrowsable(EditorBrowsableState.Always)]
        [Category("Behavior")]
        [DefaultValue(false)]
        public bool AutoRegister { get; set; }

        #endregion

        #region jQuery UI settings properties

        private double value;

        /// <summary>
        /// Gets or sets the Progressbar's initial value.
        /// </summary>
        [Setting("value")]
        [DefaultValue(0.0)]
        public double Value
        {
            get { return this.value; }
            set
            {
                if (value < 0 || value > 100)
                    throw new ArgumentOutOfRangeException("", "Value must be between 0 and 100.");

                this.value = value;
            }
        }

        #endregion

        #region jQuery client events

        /// <summary>
        /// Defines the client events for the <see cref="Progressbar"/> control.
        /// </summary>
        public sealed class ClientEventDefinitions
        {
            #region properties

            [Setting("change")]
            internal ClientEventHandler ChangeHandler
            {
                get { return this.Change == null ? null : new ClientEventHandler("event,ui", this.Change); }
            }

            /// <summary>
            /// Gets or sets a javascript snippet, thatis triggered when the value of the
            /// progressbar changes. It takes the two arguments <c>event</c> and <c>ui</c>.
            /// </summary>
            [Browsable(true)]
            [Localizable(false)]
            [EditorBrowsable(EditorBrowsableState.Always)]
            [Category("Behavior")]
            [PersistenceMode(PersistenceMode.InnerProperty)]
            public string Change
            {
                get;
                set;
            }

            #endregion

            #region constructors

            /// <summary>
            /// Creates a new <see cref="ClientEventDefinitions"/> instance.
            /// </summary>
            internal ClientEventDefinitions()
            {
            }

            #endregion
        }

        private readonly ClientEventDefinitions clientEvents = new ClientEventDefinitions();

        /// <summary>
        /// Holds the definitions for the client events.
        /// </summary>
        [SettingGroup]
        [PersistenceMode(PersistenceMode.InnerProperty)]
        public ClientEventDefinitions ClientEvents
        {
            get
            {
                return this.clientEvents;
            }
        }

        #endregion

        #region jQuery client methods

        /// <summary>
        /// Defines the client methods for the <see cref="Progressbar"/> control.
        /// </summary>
        public class ClientMethodDefinitions
        {
            #region fields

            private readonly Progressbar owner;

            #endregion

            #region constructors

            internal ClientMethodDefinitions(Progressbar owner)
            {
                this.owner = owner;
            }

            #endregion

            #region methods

            private string GenerateMethodCall(string methodName, params object[] args)
            {
                String values = String.Empty;
                if (null != args && args.Length > 0)
                {
                    StringBuilder sb = new StringBuilder();
                    foreach (object value in args)
                    {
                        sb.AppendFormat(CultureInfo.InvariantCulture, ",{0}", SettingSerializer.SerializeValue(value));
                    }
                    values = sb.ToString();
                }
                return String.Format(CultureInfo.InvariantCulture, "jQuery('#{0}').progressbar('{1}'{2})",
                                     this.owner.ClientID, methodName, values);
            }

            /// <summary>
            /// Gets a javascript snippet for the client destroy method.
            /// </summary>
            /// <returns>A javascript snippet for the client destroy method.</returns>
            public string Destroy()
            {
                return this.GenerateMethodCall("destroy");
            }

            /// <summary>
            /// Gets a javascript snippet for the client disable method.
            /// </summary>
            /// <returns>A javascript snippet for the client disable method.</returns>
            public string Disable()
            {
                return this.GenerateMethodCall("disable");
            }

            /// <summary>
            /// Gets a javascript snippet for the client enable method.
            /// </summary>
            /// <returns>A javascript snippet for the client enable method.</returns>
            public string Enable()
            {
                return this.GenerateMethodCall("enable");
            }

            /// <summary>
            /// Gets a javascript snippet for the client value method.
            /// </summary>
            /// <returns>A javascript snippet for the client value method.</returns>
            public string Value()
            {
                // This is buggy in jQuery UI 1.7.2. Use 'option' instead.
                //return this.GenerateMethodCall("value");
                return this.GenerateMethodCall("option", "value");
            }

            /// <summary>
            /// Gets a javascript snippet for the client value method.
            /// </summary>
            /// <param name="value">The value to set the progressbar's value to.</param>
            /// <returns>A javascript snippet for the client value method.</returns>
            public string Value(double value)
            {
                return this.GenerateMethodCall("value", value);
            }

            /// <summary>
            /// Gets a javascript snippet to increment the progressbar's value.
            /// </summary>
            /// <param name="value">The amount to increment the progressbar's value by.</param>
            /// <returns>A javascript snippet to increment the progressbar's value.</returns>
            public string Increment(double value)
            {
                return String.Format(CultureInfo.InvariantCulture, "jQuery('#{0}').progressbar('value', jQuery('#{0}').progressbar('option', 'value') + {1})",
                     this.owner.ClientID,
                     value);
            }

            #endregion
        }

        private ClientMethodDefinitions clientMethods;

        /// <summary>
        /// Gets a factory for creating javascript snippets for the <see cref="Dialog"/>'s
        /// client methods for use in event handlers.
        /// </summary>
        public ClientMethodDefinitions ClientMethods
        {
            get
            {
                if (null == this.clientMethods)
                {
                    this.clientMethods = new ClientMethodDefinitions(this);
                }
                return this.clientMethods;
            }
        }

        #endregion

        #region events

        /// <summary>
        /// This event is raised, just before the control applies its javascript
        /// settings to the initialization code. You can use it to manipulate the
        /// settings dictionary.
        /// </summary>
        /// <remarks>
        /// Be careful when using this event. It allows you to reach deeply into
        /// the internal workings of the control, possibly breaking functionality.
        /// </remarks>
        [EditorBrowsable(EditorBrowsableState.Advanced)]
        [Localizable(false)]
        [Category("Behavior")]
        public event EventHandler<SettingsEventArgs> ApplyingSettings;

        /// <summary>
        /// Fires the <see cref="ApplyingSettings"/> event.
        /// </summary>
        /// <param name="e">The <see cref="SettingsEventArgs"/> passed to the event.</param>
        protected virtual void OnApplyingSettings(SettingsEventArgs e)
        {
            if (null != this.ApplyingSettings)
            {
                this.ApplyingSettings(this, e);
            }
        }

        #endregion

        #region constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="Progressbar"/> class.
        /// </summary>
        public Progressbar()
            : base(HtmlTextWriterTag.Div)
        {
        }

        #endregion

        #region page lifecycle

        /// <summary>
        /// Raises the <see cref="E:System.Web.UI.Control.Init"/> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
        protected override void OnInit(EventArgs e)
        {
            if (this.AutoRegister)
            {
                this.Page.RegisterJQueryUI();
            }

            base.OnInit(e);
        }

        /// <summary>
        /// Registers client script for generating postback events prior to rendering on the client, if <see cref="P:System.Web.UI.WebControls.TextBox.AutoPostBack"/> is true.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            if (this.Visible)
            {
                Dictionary<string, object> settings = SettingSerializer.FetchSettings(this);
                this.OnApplyingSettings(new SettingsEventArgs(settings));
                string settingsJson = SettingSerializer.Serialize(settings);
                this.Page.RegisterJQueryOnDocumentReadyBlock(String.Format(
                        CultureInfo.InvariantCulture,
                        "$('#{0}').progressbar({1})",
                        this.ClientID,
                        settingsJson
                        ));
            }
        }

        /// <summary>
        /// Renders the <see cref="Progressbar"/> control to the specified <see cref="T:System.Web.UI.HtmlTextWriter"/> object.
        /// </summary>
        /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> that receives the rendered output.</param>
        protected override void Render(HtmlTextWriter writer)
        {
            string themeName = this.ThemeName;
            if (String.IsNullOrEmpty(themeName))
            {
                themeName = this.Page.GetDefaultJQueryUITheme();
            }
            if (!String.IsNullOrEmpty(themeName))
            {
                writer.AddAttribute(HtmlTextWriterAttribute.Class, "jquery-ui-" + themeName);
                writer.RenderBeginTag(HtmlTextWriterTag.Div);
            }

            base.Render(writer);

            if (!String.IsNullOrEmpty(themeName))
            {
                writer.RenderEndTag();
            }
        }

        #endregion
    }
}
